﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace SmartMathLibrary
{
    /// <summary>
    /// This class represents a mathematical vector with 3 components. It should be primary used
    /// for geometric problems in a three dimensional space.
    /// </summary>
    [Serializable]
    public class GeneralVector3D : GeneralVector
    {
        /// <summary>
        /// Initializes a new instance of the <see cref="GeneralVector3D"/> class.
        /// </summary>
        public GeneralVector3D()
            : base(3)
        {
            this.SetComponent(0, 0);
            this.SetComponent(1, 0);
            this.SetComponent(2, 0);
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="GeneralVector3D"/> class.
        /// </summary>
        /// <param name="vector">The vector which should converted to a GeneralVector3D. Only the first
        /// three components of the specified vector will be set. If the vector includes more
        /// elements they will be thrown away.</param>
        public GeneralVector3D(GeneralVector vector)
            : base(3)
        {
            if (vector == (GeneralVector) null)
            {
                throw new ArgumentNullException("vector");
            }

            this.SetComponent(0, vector.GetComponent(0));
            this.SetComponent(1, vector.GetComponent(1));
            this.SetComponent(2, vector.GetComponent(2));
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="GeneralVector3D"/> class.
        /// </summary>
        /// <param name="vector">Another GeneralVector3D instance that should be cloned.</param>
        public GeneralVector3D(GeneralVector3D vector)
            : base(3)
        {
            if (vector == (GeneralVector3D) null)
            {
                throw new ArgumentNullException("vector");
            }

            this.SetComponent(0, vector.GetComponent(0));
            this.SetComponent(1, vector.GetComponent(1));
            this.SetComponent(2, vector.GetComponent(2));
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="GeneralVector3D"/> class.
        /// </summary>
        /// <param name="x">The first component of the vector(x).</param>
        /// <param name="y">The second component of the vector(y).</param>
        /// <param name="z">The third component of the vector(z).</param>
        public GeneralVector3D(double x, double y, double z)
            : base(3)
        {
            this.SetComponent(0, x);
            this.SetComponent(1, y);
            this.SetComponent(2, z);
        }

        /// <summary>
        /// Gets or sets the x value of the GeneralVector3D.
        /// </summary>
        /// <value>The x value of the GeneralVector3D.</value>
        public double X
        {
            get { return this.GetComponent(0); }
            set { this.SetComponent(0, value); }
        }

        /// <summary>
        /// Gets or sets the y value of the GeneralVector3D.
        /// </summary>
        /// <value>The y value of the GeneralVector3D.</value>
        public double Y
        {
            get { return this.GetComponent(1); }
            set { this.SetComponent(1, value); }
        }

        /// <summary>
        /// Gets or sets the z value of the GeneralVector3D.
        /// </summary>
        /// <value>The z value of the GeneralVector3D.</value>
        public double Z
        {
            get { return this.GetComponent(2); }
            set { this.SetComponent(2, value); }
        }

        /// <summary>
        /// Implements the operator +.
        /// </summary>
        /// <param name="a">The first GeneralVector3D.</param>
        /// <param name="b">The second GeneralVector3D.</param>
        /// <returns>The result of the operator.</returns>
        public static GeneralVector3D operator +(GeneralVector3D a, GeneralVector3D b)
        {
            if (a == (GeneralVector3D) null)
            {
                throw new ArgumentNullException("a");
            }

            if (b == (GeneralVector3D) null)
            {
                throw new ArgumentNullException("b");
            }

            return new GeneralVector3D(a.ToVector() + b.ToVector());
        }

        /// <summary>
        /// Implements the operator -.
        /// </summary>
        /// <param name="a">The first GeneralVector3D.</param>
        /// <param name="b">The second GeneralVector3D.</param>
        /// <returns>The result of the operator.</returns>
        public static GeneralVector3D operator -(GeneralVector3D a, GeneralVector3D b)
        {
            if (a == (GeneralVector3D) null)
            {
                throw new ArgumentNullException("a");
            }

            if (b == (GeneralVector3D) null)
            {
                throw new ArgumentNullException("b");
            }

            return new GeneralVector3D(a.ToVector() - b.ToVector());
        }

        /// <summary>
        /// Implements the operator *.
        /// </summary>
        /// <param name="a">The first GeneralVector3D.</param>
        /// <param name="b">The second GeneralVector3D.</param>
        /// <returns>The result of the operator.</returns>
        public static double operator *(GeneralVector3D a, GeneralVector3D b)
        {
            if (a == (GeneralVector3D) null)
            {
                throw new ArgumentNullException("a");
            }

            if (b == (GeneralVector3D) null)
            {
                throw new ArgumentNullException("b");
            }

            return a.ToVector() * b.ToVector();
        }

        /// <summary>
        /// Implements the operator *.
        /// </summary>
        /// <param name="value">The scalar value with which the vector should be multiplied.</param>
        /// <param name="a">The GeneralVector3D instance.</param>
        /// <returns>The result of the operator.</returns>
        public static GeneralVector3D operator *(double value, GeneralVector3D a)
        {
            if (a == (GeneralVector3D) null)
            {
                throw new ArgumentNullException("a");
            }

            return new GeneralVector3D(value * a.ToVector());
        }

        /// <summary>
        /// Implements the operator *.
        /// </summary>
        /// <param name="a">The GeneralVector3D instance.</param>
        /// <param name="value">The scalar value with which the vector should be multiplied.</param>
        /// <returns>The result of the operator.</returns>
        public static GeneralVector3D operator *(GeneralVector3D a, double value)
        {
            if (a == (GeneralVector3D) null)
            {
                throw new ArgumentNullException("a");
            }

            return new GeneralVector3D(a.ToVector() * value);
        }

        /// <summary>
        /// Converts the GeneralVector3D to a normal n-dimensional vector.
        /// </summary>
        /// <returns>The converted vector.</returns>
        public GeneralVector ToVector()
        {
            return new GeneralVector(this.VectorData);
        }
    }
}